import { defineStore, storeToRefs } from 'pinia';
import { ref } from 'vue';
import { SOCKET_STATUS } from '@/types';
import { user } from './user';
import { global } from './global';

export const socket = defineStore('socket', () => {
    const socket = ref();
    const status = ref(SOCKET_STATUS.OFFLINE); // true在线 false离线
    let hbTimer: any;
    const hbDelay = 25000;
    let rcTimer: any;
    let count = 0;
    let socketStatus = 0;
    let channelId = '';
    const rcDelay = 10000;

    const callBackList = ref<((...args: []) => void)[]>([]);

    async function initSocket() {
        const { token } = storeToRefs(user());
        init();

        function beforeInit() {
            if (socketStatus > 0) return false;
            if (!token.value) return false;
            socket.value?.close(); // 关闭之前的socket
            return true;
        }

        function init() {
            if (!beforeInit()) return;

            socket.value = uni.connectSocket({
                url: `${import.meta.env.VITE_WS_URL}?accessToken=${token.value}`,
                complete: () => {},
            });

            socket.value.onOpen(() => {
                console.log('Socket已打开！');
                count = 0;
                socketStatus = 10;
                clearInterval(hbTimer);
                clearInterval(rcTimer);
                heartBeat(); // 心跳
                status.value = SOCKET_STATUS.CONNECTED;
            });
            socket.value.onError((err: any) => {
                console.log('Socket打开失败！');
                console.error(err);
                socketStatus = 0;
                status.value = SOCKET_STATUS.OFFLINE;
                reconnect();
            });
            socket.value.onClose((err: any) => {
                console.log('Socket已断开！');
                console.error(err);
                socketStatus = 0;
                status.value = SOCKET_STATUS.OFFLINE;
                reconnect();
            });
            socket.value.onMessage((res: anyObj) => {
                const info = JSON.parse(res.data);
                if (info.channelId) {
                    channelId = info.channelId;
                }
                callback(info);
            });
        }

        // 消息回调
        function callback(res: anyObj) {
            callBackList.value.forEach((cb: any) => {
                cb(res);
            });
        }

        // 心跳
        function _heartBeat() {
            if (!token.value) {
                socket.value?.close();
                clearInterval(hbTimer);
                return;
            }
            socketSend({
                message: 'ping',
                type: 'AUTHORIZATION',
                accessToken: token.value,
            });
        }
        function heartBeat() {
            _heartBeat(); // 首次连上必须ping才建立通道
            hbTimer = setInterval(() => _heartBeat(), hbDelay);
        }

        // 重连
        function reconnect(leading = false) {
            clearInterval(hbTimer);
            if (leading) {
                init();
                return;
            }
            rcTimer = setTimeout(() => {
                count++;
                console.error(`Socket尝试重连（第${count}次）`);
                init();
            }, rcDelay);
        }
    }

    // 发送消息
    function socketSend(value: any) {
        socket.value.send({
            data: JSON.stringify({
                channelId,
                ...value,
            }),
        });
    }

    // 增加回调
    function addCallBack(cb: any) {
        if (callBackList.value.some(i => i === cb)) return;
        callBackList.value.push(cb);
    }

    // 删除回调
    function removeCallBack(cb: any) {
        const idx = callBackList.value.findIndex(i => i === cb);
        if (idx === -1) return;
        callBackList.value.splice(idx, 1);
    }

    // 关闭socket
    function closeSocket() {
        socket.value?.close();
    }

    return {
        socket,
        status,
        callBackList,
        initSocket,
        socketSend,
        addCallBack,
        removeCallBack,
        closeSocket,
    };
});
